package nfwx.analytics;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.util.List;
import java.util.Map;

/**
 * 将GrabValue转换为JSON的函数
 */

public class GrabJSON {

    private static boolean checkValue(String name, GrabValue v, boolean onlyChanged) throws JSONException {
        if (!v.getHasValue()) {
            if (v.getMustHasValue()) throw new JSONException(name + " must have value but it does not");
            return false;
        }
        if (v.getMustHasValue() || v.getChanged() || !onlyChanged) {
            return true;
        }
        return false;
    }

    private static String JSONString(String value) {
        StringBuilder out = new StringBuilder();
        out.append("\"");
        for (int i = 0, length = value.length(); i < length; i++) {
            char c = value.charAt(i);

            /*
             * From RFC 4627, "All Unicode characters may be placed within the
             * quotation marks except for the characters that must be escaped:
             * quotation mark, reverse solidus, and the control characters
             * (U+0000 through U+001F)."
             */
            switch (c) {
                case '"':
                case '\\':
                case '/':
                    out.append('\\').append(c);
                    break;

                case '\t':
                    out.append("\\t");
                    break;

                case '\b':
                    out.append("\\b");
                    break;

                case '\n':
                    out.append("\\n");
                    break;

                case '\r':
                    out.append("\\r");
                    break;

                case '\f':
                    out.append("\\f");
                    break;

                default:
                    if (c <= 0x1F) {
                        out.append(String.format("\\u%04x", (int) c));
                    } else {
                        out.append(c);
                    }
                    break;
            }

        }
        out.append("\"");
        return out.toString();
    }

    /**
     * 将GrabObject转换为JSONObject
     * @param name 对象名
     * @param v 要转换的对象
     * @param onlyChanged 仅转换修改的成员
     * @return 转换后的JSONObject, 如无需转换则返回null
     * @throws JSONException
     */
    public static JSONObject toJSONObject(String name, GrabValue v, boolean onlyChanged) throws JSONException {
        if (!(v instanceof GrabObject)) {
            throw new JSONException(name + " is not an GrabObject");
        }
        if (!checkValue(name, v, onlyChanged)) return null;
        GrabObject gv = (GrabObject)v;
        Map<String, GrabValue> vals = gv.getValues();
        JSONObject res = new JSONObject();
        for(Map.Entry<String, GrabValue> kv:vals.entrySet()) {
            GrabValue val = kv.getValue();
            if (!checkValue(name + "." + kv.getKey(), val, onlyChanged)) continue;
            if (val instanceof GrabObject) {
                JSONObject r = toJSONObject(name + "." + kv.getKey(), val, onlyChanged);
                if (r != null) {
                    res.put(kv.getKey(), r);
                }
            } else if (val instanceof GrabArray) {
                JSONArray r = toJSONArray(name + "." + kv.getKey(), val, onlyChanged);
                if (r != null) {
                    res.put(kv.getKey(), r);
                }
            } else if (val instanceof GrabString) {
                GrabString gs = (GrabString)val;
                res.put(kv.getKey(), gs.getValue());
            } else if (val instanceof GrabNumber) {
                GrabNumber gs = (GrabNumber)val;
                res.put(kv.getKey(), gs.getValue());
            } else if (val instanceof GrabBoolean) {
                GrabBoolean gs = (GrabBoolean)val;
                res.put(kv.getKey(), gs.getValue());
            }
        }
        return res;
    }
    /**
     * 将GrabArray转换为JSONArray
     * @param name 对象名
     * @param v 要转换的对象
     * @param onlyChanged 仅转换修改的成员
     * @return 转换后的JSONArray, 如果无需转换则返回null
     * @throws JSONException
     */
    public static JSONArray toJSONArray(String name, GrabValue v, boolean onlyChanged) throws JSONException {
        if (!(v instanceof GrabArray)) {
            throw new JSONException(name + " is not an GrabArray");
        }
        if (!checkValue(name, v, onlyChanged)) return null;
        GrabArray gv = (GrabArray)v;
        List<GrabValue> vals = gv.getValues();
        JSONArray res = new JSONArray();
        for(int i=0; i<vals.size(); ++i) {
            GrabValue val = vals.get(i);
            if (!checkValue(name + "." + i, val, onlyChanged)) continue;
            if (val instanceof GrabObject) {
                JSONObject r = toJSONObject(name + "." + i, val, onlyChanged);
                if (r != null) {
                    res.put(r);
                }
            } else if (val instanceof GrabArray) {
                JSONArray r = toJSONArray(name + "." + i, val, onlyChanged);
                if (r != null) {
                    res.put( r);
                }
            } else if (val instanceof GrabString) {
                GrabString gs = (GrabString)val;
                res.put(gs.getValue());
            } else if (val instanceof GrabNumber) {
                GrabNumber gs = (GrabNumber)val;
                res.put(gs.getValue());
            } else if (val instanceof GrabBoolean) {
                GrabBoolean gs = (GrabBoolean)val;
                res.put(gs.getValue());
            }
        }
        return res;
    }

    /**
     * 将GrabValue转换为JSON字符串
     * @param name 对象名
     * @param v 要转换的对象
     * @param onlyChanged 仅转换修改的成员
     * @return 转换后的JSON字符串, 如果无需转换则返回null
     * @throws JSONException
     */
    public static String toJSONString(String name, GrabValue v, boolean onlyChanged) throws JSONException {
        if (!checkValue(name, v, onlyChanged)) return null;
        if (v instanceof GrabObject) {
            JSONObject r = toJSONObject(name, v, onlyChanged);
            if (r == null) return null;
            return r.toString();
        } else if (v instanceof GrabArray) {
            JSONArray r = toJSONArray(name, v, onlyChanged);
            if (r == null) return null;
            return r.toString();
        } else if (v instanceof GrabString) {
            GrabString gs = (GrabString)v;
            return JSONString(gs.getValue());
        } else if (v instanceof GrabNumber) {
            GrabNumber gs = (GrabNumber)v;
            return ""+gs.getValue();
        } else if (v instanceof GrabBoolean) {
            GrabBoolean gs = (GrabBoolean)v;
            if (gs.getValue()) return "true";
            else return "false";
        } else {
            throw new JSONException(name + " is unknown type");
        }
    }
}
